package com.jk.vehicle.core.utils;

import com.alibaba.fastjson.JSON;
import com.esotericsoftware.reflectasm.MethodAccess;
import com.jk.vehicle.core.exception.DataParseException;
import com.jk.vehicle.core.exception.InstanceException;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.ResultSet;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ConcurrentSkipListSet;

/**
 * 实例辅助类
 *
 * @author ShenHuaJie
 * @since 2012-07-18
 */
public final class InstanceUtil {

	/**
	 * 日志
	 */
	protected static Logger logger = LogManager.getLogger();

	private InstanceUtil() {
	}

	/**
	 * 获取方法map
	 */
	private static Map<String, MethodAccess> methodMap = InstanceUtil.newHashMap();

	/** 实例化并复制属性 */
	public static <T> T to(Object orig, Class<T> clazz) {
		T bean = null;
		try {
			bean = clazz.newInstance();
			Class<?> cls = orig.getClass();
			BeanInfo orgInfo = Introspector.getBeanInfo(cls);
			PropertyDescriptor[] orgPty = orgInfo.getPropertyDescriptors();
			Map<String, PropertyDescriptor> propertyMap = newHashMap();
			for (PropertyDescriptor property : orgPty) {
				propertyMap.put(property.getName(), property);
			}
			BeanInfo beanInfo = Introspector.getBeanInfo(clazz);
			PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
			for (PropertyDescriptor property : propertyDescriptors) {
				String key = property.getName();
				// 过滤class属性
				if (!"class".equals(key) && propertyMap.containsKey(key)) {
					Method getter = propertyMap.get(key).getReadMethod();
					Method setter = property.getWriteMethod();
					Object value = "";
					try {
						value = getter.invoke(orig);
						value = TypeParseUtil.convert(value, property.getPropertyType(), null);
						setter.invoke(bean, value);
					} catch (Exception e) {
						logger.error("to Error " + key + ":" + value + ">" + e);
					}
				}
			}
		} catch (Exception e) {
			logger.error("to Error " + e);
		}
		return bean;
	}

	/**
	 * json转bean
	 * @param json
	 * @param clazz
	 * @param <T>
	 * @return
	 */
	@SuppressWarnings({"rawtypes", "unchecked"})
	public static <T> T parse(String json, Class<T> clazz) {
		try {
			Map map = JSON.parseObject(json, Map.class);
			return (T) transMap2Bean(map, clazz);
		} catch (Exception e) {
			logger.error("parse", e);
		}
		return null;
	}

	/**
	 *  Map --> Bean 1: 利用Introspector,PropertyDescriptor实现 Map --> Bean
	 * @param map
	 * @param clazz
	 * @param <T>
	 * @return
	 */
	public static <T> T transMap2Bean(Map<String, Object> map, Class<T> clazz) {
		T bean = null;
		try {
			bean = clazz.newInstance();
			BeanInfo beanInfo = Introspector.getBeanInfo(clazz);
			PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
			for (PropertyDescriptor property : propertyDescriptors) {
				String key = property.getName();
				if (map.containsKey(key)) {
					Object value = map.get(key);
					// 得到property对应的setter方法
					Method setter = property.getWriteMethod();
					setter.invoke(bean, TypeParseUtil.convert(value, property.getPropertyType(), null));
				}
			}
		} catch (Exception e) {
			logger.error("transMap2Bean Error ", e);
		}
		return bean;
	}

	/**
	 * ean --> Map 1: 利用Introspector和PropertyDescriptor 将Bean --> Map
	 * @param obj
	 * @return
	 */
	public static Map<String, Object> transBean2Map(Object obj) {
		Map<String, Object> map = newHashMap();
		if (obj == null) {
			return map;
		}
		try {
			BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());
			PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
			for (PropertyDescriptor property : propertyDescriptors) {
				String key = property.getName();
				// 过滤class属性
				if (!"class".equals(key)) {
					// 得到property对应的getter方法
					Method getter = property.getReadMethod();
					Object value = getter.invoke(obj);
					map.put(key, value);
				}
			}
		} catch (Exception e) {
			logger.error("transBean2Map Error " + e);
		}
		return map;
	}

	/**
	 * @param oldBean
	 * @param newBean
	 * @return
	 */
	public static <T> T getDiff(T oldBean, T newBean) {
		if (oldBean == null && newBean != null) {
			return newBean;
		} else if (newBean == null) {
			return null;
		} else {
			Class<?> cls1 = oldBean.getClass();
			try {
				@SuppressWarnings("unchecked") T object = (T) cls1.newInstance();
				BeanInfo beanInfo = Introspector.getBeanInfo(cls1);
				PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
				for (PropertyDescriptor property : propertyDescriptors) {
					String key = property.getName();
					// 过滤class属性
					if (!"class".equals(key)) {
						// 得到property对应的getter方法
						Method getter = property.getReadMethod();
						// 得到property对应的setter方法
						Method setter = property.getWriteMethod();
						Object oldValue = getter.invoke(oldBean);
						Object newValue = getter.invoke(newBean);
						if (setter != null && newValue != null && !newValue.equals(oldValue)) {
							setter.invoke(object, newValue);
						}
					}
				}
				return object;
			} catch (Exception e) {
				throw new DataParseException(e);
			}
		}
	}

	/**
	 * Return the specified class. Checks the ThreadContext classloader first,
	 * then uses the System classloader. Should replace all calls to
	 * <code>Class.forName( claz )</code> (which only calls the System class
	 * loader) when the class might be in a different classloader (e.g. in a
	 * webapp).
	 *
	 * @param clazz
	 *            the name of the class to instantiate
	 * @return the requested Class object
	 */
	public static Class<?> getClass(String clazz) {
		/**
		 * Use the Thread context classloader if possible
		 */
		ClassLoader loader = Thread.currentThread().getContextClassLoader();
		try {
			if (loader != null) {
				return Class.forName(clazz, true, loader);
			}
			/**
			 * Thread context classloader isn't working out, so use system
			 * loader.
			 */
			return Class.forName(clazz);
		} catch (ClassNotFoundException e) {
			throw new InstanceException(e);
		}
	}

	/**
	 * 封装实体
	 *
	 * @param cls
	 *            实体类
	 * @param list
	 *            实体Map集合
	 * @return
	 */
	public static <E> List<E> getInstanceList(Class<E> cls, List<?> list) {
		List<E> resultList = newArrayList();
		E object = null;
		for (Object name : list) {
			Map<?, ?> map = (Map<?, ?>) name;
			object = newInstance(cls, map);
			resultList.add(object);
		}
		return resultList;
	}

	/**
	 * 封装实体
	 *
	 * @param cls
	 *            实体类
	 * @param rs
	 *            数据查询结果集
	 * @return
	 */
	public static <E> List<E> getInstanceList(Class<E> cls, ResultSet rs) {
		List<E> resultList = newArrayList();
		try {
			E object = cls.newInstance();
			Field[] fields = cls.getDeclaredFields();
			while (rs.next()) {
				object = cls.newInstance();
				for (Field field : fields) {
					String fieldName = field.getName();
					PropertyUtils.setProperty(object, fieldName, rs.getObject(fieldName));
				}
				resultList.add(object);
			}
		} catch (Exception e) {
			throw new InstanceException(e);
		}
		return resultList;
	}

	/**
	 * 新建实例
	 *
	 * @param cls
	 *            实体类
	 * @param map
	 *            实体属性Map
	 * @return
	 */
	public static <E> E newInstance(Class<E> cls, Map<String, ?> map) {
		E object = null;
		try {
			object = cls.newInstance();
			BeanUtils.populate(object, map);
		} catch (Exception e) {
			throw new InstanceException(e);
		}
		return object;
	}

	/**
	 * Return a new instance of the given class. Checks the ThreadContext
	 * classloader first, then uses the System classloader. Should replace all
	 * calls to <code>Class.forName( claz ).newInstance()</code> (which only
	 * calls the System class loader) when the class might be in a different
	 * classloader (e.g. in a webapp).
	 *
	 * @param clazz
	 *            the name of the class to instantiate
	 * @return an instance of the specified class
	 */
	public static Object newInstance(String clazz) {
		try {
			return getClass(clazz).newInstance();
		} catch (Exception e) {
			throw new InstanceException(e);
		}
	}

	/**
	 * 获取实例
	 * @param cls
	 * @param args
	 * @param <K>
	 * @return
	 */
	public static <K> K newInstance(Class<K> cls, Object... args) {
		try {
			Class<?>[] argsClass = null;
			if (args != null) {
				argsClass = new Class[args.length];
				for (int i = 0, j = args.length; i < j; i++) {
					argsClass[i] = args[i].getClass();
				}
			}
			Constructor<K> cons = cls.getConstructor(argsClass);
			return cons.newInstance(args);
		} catch (Exception e) {
			throw new InstanceException(e);
		}
	}

	/**
	 * 类映射Map
	 */
	private static Map<String, Class<?>> clazzMap = new HashMap<String, Class<?>>();

	/**
	 * 新建实例
	 *
	 * @param className
	 *            类名
	 * @param args
	 *            构造函数的参数
	 * @return 新建的实例
	 */
	public static Object newInstance(String className, Object... args) {
		try {
			Class<?> newoneClass = clazzMap.get(className);
			if (newoneClass == null) {
				newoneClass = Class.forName(className);
				// 缓存class对象
				clazzMap.put(className, newoneClass);
			}
			return newInstance(newoneClass, args);
		} catch (Exception e) {
			throw new InstanceException(e);
		}
	}

	/**
	 * 执行某对象方法
	 *
	 * @param owner
	 *            对象
	 * @param methodName
	 *            方法名
	 * @param args
	 *            参数
	 * @return 方法返回值
	 */
	public static Object invokeMethod(Object owner, String methodName, Object... args) {
		Class<?> ownerClass = owner.getClass();
		String key = null;
		if (args != null) {
			Class<?>[] argsClass = new Class[args.length];
			for (int i = 0, j = args.length; i < j; i++) {
				if (args[i] != null) {
					argsClass[i] = args[i].getClass();
				}
			}
			// 用于区分重载的方法
			key = ownerClass + "_" + methodName + "_" + StringUtils.join(argsClass, ",");
		} else {
			// 用于区分重载的方法
			key = ownerClass + "_" + methodName;
		}
		MethodAccess methodAccess = methodMap.get(key);
		if (methodAccess == null) {
			methodAccess = MethodAccess.get(ownerClass);
			// 缓存Method对象
			methodMap.put(key, methodAccess);
		}
		return methodAccess.invoke(owner, methodName, args);
	}

	/**
	 * Constructs an empty ArrayList.
	 */
	public static <E> ArrayList<E> newArrayList() {
		return new ArrayList<E>();
	}

	/**
	 * Constructs an empty ArrayList.
	 */
	@SuppressWarnings("unchecked")
	public static <E> ArrayList<E> newArrayList(E... e) {
		ArrayList<E> list = newArrayList();
		Collections.addAll(list, e);
		return list;
	}

	/**
	 * Constructs an empty HashMap.
	 */
	public static <k, v> HashMap<k, v> newHashMap() {
		return new HashMap<k, v>();
	}

	/**
	 * Constructs an empty HashSet.
	 */
	public static <E> HashSet<E> newHashSet() {
		return new HashSet<E>();
	}

	/**
	 * Constructs an empty Hashtable.
	 */
	public static <k, v> Hashtable<k, v> newHashtable() {
		return new Hashtable<k, v>();
	}

	/**
	 * Constructs an empty LinkedHashMap.
	 */
	public static <k, v> LinkedHashMap<k, v> newLinkedHashMap() {
		return new LinkedHashMap<k, v>();
	}

	/**
	 * Constructs an empty LinkedHashSet.
	 */
	public static <E> LinkedHashSet<E> newLinkedHashSet() {
		return new LinkedHashSet<E>();
	}

	/**
	 * Constructs an empty LinkedList.
	 */
	public static <E> LinkedList<E> newLinkedList() {
		return new LinkedList<E>();
	}

	/**
	 * Constructs an empty TreeMap.
	 */
	public static <k, v> TreeMap<k, v> newTreeMap() {
		return new TreeMap<k, v>();
	}

	/**
	 * Constructs an empty TreeSet.
	 */
	public static <E> TreeSet<E> newTreeSet() {
		return new TreeSet<E>();
	}

	/**
	 * Constructs an empty Vector.
	 */
	public static <E> Vector<E> newVector() {
		return new Vector<E>();
	}

	/**
	 * Constructs an empty WeakHashMap.
	 */
	public static <k, v> WeakHashMap<k, v> newWeakHashMap() {
		return new WeakHashMap<k, v>();
	}

	/**
	 * Constructs an HashMap.
	 */
	public static <k, v> HashMap<k, v> newHashMap(k key, v value) {
		HashMap<k, v> map = newHashMap();
		map.put(key, value);
		return map;
	}

	/**
	 * Constructs an HashMap.
	 */
	public static <k, v> HashMap<k, v> newHashMap(k[] key, v[] value) {
		HashMap<k, v> map = newHashMap();
		for (int i = 0; i < key.length; i++) {
			map.put(key[i], value[i]);
		}
		return map;
	}

	/**
	 * Constructs an LinkedHashMap.
	 */
	public static <k, v> LinkedHashMap<k, v> newLinkedHashMap(k key, v value) {
		LinkedHashMap<k, v> map = newLinkedHashMap();
		map.put(key, value);
		return map;
	}

	/**
	 * Constructs an empty ConcurrentHashMap.
	 */
	public static <k, v> ConcurrentHashMap<k, v> newConcurrentHashMap() {
		return new ConcurrentHashMap<k, v>();
	}

	/**
	 * Constructs an empty ConcurrentLinkedDeque.
	 */
	public static <e> ConcurrentLinkedDeque<e> newConcurrentLinkedDeque() {
		return new ConcurrentLinkedDeque<e>();
	}

	/**
	 * Constructs an empty ConcurrentLinkedQueue.
	 */
	public static <e> ConcurrentLinkedQueue<e> newConcurrentLinkedQueue() {
		return new ConcurrentLinkedQueue<e>();
	}

	/**
	 * Constructs an empty ConcurrentSkipListSet.
	 */
	public static <e> ConcurrentSkipListSet<e> newConcurrentSkipListSet() {
		return new ConcurrentSkipListSet<e>();
	}

	/**
	 * @param e
	 * @return
	 */
	public static <E> Set<E> newHashSet(E[] e) {
		Set<E> set = newHashSet();
		Collections.addAll(set, e);
		return set;
	}
}
